This section describes the terms and concepts that underlie the use of modules in ScriptX:
Because a ScriptX variable can store a reference to any object, including classes and functions, exporting the name of a class or function makes those objects public, ready for import into other modules.
In Figure 9-2,
MyOtherModule
uses MyModule
. The
names x
, y
, and z
that
are exported by MyModule
are imported by
MyOtherModule
. The two variables named
x
in the two modules both point to the same value
cell. Thus, if you set x
to a new value in one
module, its value is changed in both modules. Although the
name is exported from one module and imported into the other,
access to its value cell is completely symmetric-it can be set
or get from either module.
A binding is an association between a name and a value cell in memory. A value cell stores a pointer to an object somewhere in memory. Bindings act as a level of indirection between names and value cells. This indirection is what allows a ScriptX program to run with different namespaces. When the ScriptX compiler switches from one module to another, it is actually switching from one set of bindings to another.
Think of a binding as a way to hang on to an object. Objects
do not have to be associated with bindings. References to
objects are embedded within other objects, often many levels
deep. Expressions that assign a new global name, such as an
assignment, function
, or class
expression, create a binding.
The following example demonstrates what bindings are created when a simple ScriptX program runs. Although it is not a complete technical description, it shows in a heuristic way how objects are associated with names, through bindings, and how names are used in a ScriptX program to get access to objects. ScriptX manages a global namespace (there is one global namespace for each module), by keeping a table of name bindings.
class KittyCat ()
inst vars
favoriteFood
end
-- create an instances of Cat
object kiri (KittyCat)
settings favoriteFood:"tuna"
end
KittyCat@0xe938c8
This program creates four name bindings. The first one is used
to get access to the newly created KittyCat
class, which is itself an object. The next two bindings are
associated with instance variable access. Instance variable
access is actually through generic getter and setter
functions. These generic functions call the appropriate getter
and setter methods. ScriptX automatically creates entries for
generics in the module's name table. Finally, the script
creates an object, an instance of KittyKat
. This
object is associated with a binding, bringing the total of new
bindings in the system to four.
The following is a list of bindings that have been created so far in the execution of this program. Names in the first column are associated with objects, including classes and generic functions, in the second column. Lexical names are in lower case because they are interned in their downcase form. (In the scripter, you don't have to be concerned with case; you are free to use uppercase and lowercase to make your scripts more understandable.)
Lexical Name |
Points To |
---|---|
kittycat |
the KittyCat class |
favoritefoodgetter | a generic function |
favoritefoodsetter | a generic function |
kiri |
a KittyCat object at address
0xe938c8 . |
favoriteFood
instance variable slot for the kiri
object is
filled with a string constant (that is, with a pointer to a
StringConstant
object). This string constant has
no bindings of its own. A script can only get and set the
contents of instance variables through some binding or
bindings. A simple instance variable access is actually a
generic function call, which uses the bindings defined for the
object and for the generic functions it provides a method
for.kiri.favoriteFood
"tuna"
The instance variable access expression
kiri.favoriteFood
is translated into a call to
the getter generic function that is associated with this
instance variable. Two bindings are required to get a
value-one to the generic function and one to the object
itself.
favoriteFoodGetter kiri
Each instance variable is really associated with two name bindings, one for its getter and one for its setter. If an instance variable of the same name exists elsewhere in the current module, it shares the same binding. A generic function handles method dispatch, routing calls to a getter or setter to the method defined by the appropriate object. This allows the same instance variable name to be used by many different objects within a module.
This chapter discusses syntax for importing and exporting variables in modules. What really happens in ScriptX is that a module imports and exports a set of name bindings. As a result, ScriptX modules do not just share values-they share value cells.
The only truly global names are module names. All variables are declared within some module. A variable can only be accessed (referenced) within some module. Since a given value cell can be bound to different names in different modules, and since the same name can be used to denote different variables in different modules, you do not know what variable something is until you know what module it was compiled in.
A variable is owned by the module that defines it. Only one module owns each variable. The ScriptX class definition, object definition, function definition, and global assignment expressions (a global assignment is a global declaration and assignment all in one expression) all declare and define a variable. Note that a variable defined when an object is assigned to a name. This name is usually declared as a name and assigned a value in the same expression, as in a class, object, or function declaration.
A module does not have to be the module that defines a variable to export a name for that variable. The ScriptX module system allows variables to be exported from one module, and then imported and defined in another module. That second module, the one that defines the variable, does not have to re-export the variable for that definition to be visible, nor does a module that wants to use that variable need to use the module that defined it. By using the module that exported the variable, its definition comes along automatically. This separation between an exported variable and its definition allows you to organize and build networks of modules which can include circular use relationships between modules, multiple interfaces to the same module, and more comprehensible relationships amongst complex networks of modules that use and export many different variables. "Organizing Modules" on page 214 describes how to use modules in this way.
The module that owns a variable is the module that provides its definition. A variable that is not defined in any module is not owned by any module.
The class
, object
, and
function
declaration expressions declare and
assign the variable in one expression, allowing them to be
owned by the module that defined them. You can separate
declaration and assignment for both objects and functions. For
classes, the name must be specified when the class is
declared, and that name is automatically declared constant.
Thus, a class name is always owned by the module in which it
is declared.
A global variable that you create and assign by hand (using an assignment expression) requires two conditions to be owned by a module: you must both declare it and assign it in the same expression:
global myVariable := "variable's value"
Only declaration and assignment in the same expression confer ownership of a variable; declaring and assigning the variable in different expressions is not the same:
global anotherVariable
anotherVariable := "another value" -- anotherVariable is not owned
A variable that is declared but not assigned any value does
not get saved with a module-the module does not yet
own that variable. Note that when you define a
variable in a ScriptX program you do not necessarily have to
give it the same value it will hold when the module is stored.
When modules are stored in the ScriptX object store, the
module stores the most recent value for each variable that it
owns. To make sure a variable is owned by a module without
having to assign it its proper value right away, give it a
value of undefined
at declaration time:
global VarToBeChanged := undefined
Later on in the program, you can assign the value you want to be stored.
A module maintains its own table of name bindings. To export a
variable is to allow other modules to have a binding for a
particular value cell. To say that a particular module defines
a variable means that its value cell was created in that
module. The value cell is created when the variable is
assigned a value. Other modules that have access to the
variable have access only through the value cell in the module
where it is defined. To import a variable into a module is to
have a binding for that variable in the current module. Other
options for using modules, such as excludes
,
prefix
, and renames
, also operate on
this table of bindings.
If a module attempts to define a variable that is already shared, an error message results and the variable is not redefined. If a variable is exported (shared), then it is still only one variable (not a copy) and can have only one definition. For example, if a shared variable is defined in one module, assigned to in another module, and read in a third module, the third module reads the value that was assigned by the second module.
A shared variable does not have to have the same name in each module that uses it. You can use features like prefix and renames to give the variable multiple names, but it is still only one variable (not a copy) and has only one definition. Different modules may use different names, but they are still accessing the same location in memory.
If a variable is not exported, then you can use the same identifier to define a separate variable in another module. These variables have the same name within each module, but they refer to different locations in memory.
In this respect, a variable is really just a collection of name bindings, one for each module in which the variable is visible. A variable continues to exist and to occupy memory when the module it is defined in is no longer the module that the compiler is currently using. A variable is visible to scripts in the current module if the current module has a binding for it.
The global
expression declares a ScriptX lexical
name. A name that is declared global is visible only within
the module in which it is declared, unless that name is
exported. In this way, the same lexical name can be declared
in different modules.
In the following example, the module Europe
exports four names: finland
,
albania
, portugal
, and
ireland
.
module Europe
uses ScriptX
exports finland, albania, portugal, ireland
end
in module Europe
global finland, albania:19, portugal := 27
When the global expression is combined with an assignment
expression, the resulting expression both declares a lexical
name and defines a variable. Since albania
and
portugal
are assigned values,
albania
and portugal
are both
declared as names and defined as a variables.
At this point, a program can access the values of
albania
and portugal
, since both
names have been defined as variables. Although
finland
is declared as a name, it is not assigned
a value, so finland
is not defined as a variable.
Likewise, ireland
has not been assigned a value,
so it cannot be accessed as a variable. Since
ireland
has not been declared either, assigning a
value to ireland
will cause the compiler to issue
a warning. By contrast, finland
has been
declared, so the compiler does not issue a warning when a
value is assigned to finland
.
If compilation and execution switch to another module that
uses the Europe
module, the variables that are
defined in Europe
are visible there.
module World
uses ScriptX, Europe
end
in module World
albania
19
portugal
27
At this point in the execution of the program,
finland
and ireland
have still not
been defined as variables. When they are assigned values in
the World
module, they are defined in the
World
module. Even though finland
was declared in the Europe
module, and
ireland
was exported by Europe
, but
not declared, both variables are now defined in
World
.
in module World
finland := 11
ireland := 94
If compilation reverts to the Europe
module,
finland
and ireland
retain their
definitions as variables. They are visible in both
Europe
and World
.
in module Europe
finland
11
ireland
94
Although all four of the variables in this example are
exported by Europe
, and three of them are
declared there, it is where they are defined that determines
how they are saved. Since albania
and
portugal
are defined in the Europe module, they
are saved to the object store with Europe
. By
contrast, finland
and ireland
are
actually defined in the World
module.
Only declaration and definition in the same module confers
ownership. Since finland
is not declared in the
World
module, it cannot be owned by any module,
and is not saved. But ireland
, even though it was
exported by Europe
, is declared and defined in
the World
module. Thus, ireland
is
saved with the World
module.
The object, class, and function definition expressions implicitly declare a global name and define a variable as well, creating a binding for that variable in the current module.
module Universe
uses ScriptX
end
in module Universe
class Star () end
object sun (Star) end
function orbit a b ->
format debug "1%* orbits 2%*\n" #(a, b) @normal
In this example, sun
, star
, and
orbit
are declared as lexical names and defined
as variables (a class definition is a constant). Each of these
variables has a binding in the Universe
module,
meaning that a lexical name is associated with the variable in
that module. Since sun
, star
, and
orbit
are defined as variables in
Universe
, they can be saved to the object store
with Universe
, and they can be exported to other
classes.
This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.